// -------------------------------------------------------------------------------------------------
// Copyright 2024 Kearn Chen, kearn.chen@aliyun.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -------------------------------------------------------------------------------------------------
// Description :
//             1. Arithmetic and Logic Unit
// -------------------------------------------------------------------------------------------------

module k0a_core_alu (
    input  wire [3:0]   idu2alu_op    ,
    input  wire [31:0]  idu2alu_rs1   ,
    input  wire [31:0]  idu2alu_rs2   ,
    output wire [31:0]  alu2idu_res   ,
    output wire         alu2idu_cmp   ,
    input  wire [19:0]  idu2alu_addr1 ,
    input  wire [19:0]  idu2alu_addr2 ,
    output wire [19:0]  alu2idu_addro
);

wire op_add  = idu2alu_op[2:0] == 3'b000;
wire op_sll  = idu2alu_op[2:0] == 3'b001;
wire op_slt  = idu2alu_op[2:0] == 3'b010;
wire op_sltu = idu2alu_op[2:0] == 3'b011;
wire op_xor  = idu2alu_op[2:0] == 3'b100;
wire op_srl  = idu2alu_op[2:0] == 3'b101;
wire op_or   = idu2alu_op[2:0] == 3'b110;
wire op_and  = idu2alu_op[2:0] == 3'b111;

wire [31:0] alu_and = idu2alu_rs1 & idu2alu_rs2;
wire [31:0] alu_or  = idu2alu_rs1 | idu2alu_rs2;
wire [31:0] alu_xor = idu2alu_rs1 ^ idu2alu_rs2;

wire [31:0] alu_inv = idu2alu_rs2 ^ {32{idu2alu_op[3]}};

wire [32:0] alu_in1 = {1'b0, op_slt ^ idu2alu_rs1[31], idu2alu_rs1[30:0]};
wire [32:0] alu_in2 = {idu2alu_op[3], op_slt ^ alu_inv[31], alu_inv[30:0]};

wire [32:0] alu_add = alu_in1 + alu_in2 + idu2alu_op[3];

wire [31:0] alu_srs = idu2alu_op[3] ? {{31{idu2alu_rs1[31]}}, 1'b0} : idu2alu_rs1;

wire [31:0] alu_sll = alu_srs << alu_inv[4:0];
wire [31:0] alu_srl = idu2alu_rs1 >> idu2alu_rs2[4:0];
wire [31:0] alu_sra = {32{idu2alu_op[3]}} & alu_sll | alu_srl;

wire alu_zero = alu_add[31:0] == 32'd0;

wire alu_slt = (op_slt | op_sltu) & alu_add[32];

assign alu2idu_res = {32{op_add}} & alu_add[31:0]  | {32{op_and}} & alu_and |
                     {32{op_xor}} & alu_xor        | {32{op_or }} & alu_or  |
                     {32{op_sll}} & alu_sll        | {32{op_srl}} & alu_sra |
                     {31'd0, alu_slt};

assign alu2idu_cmp = op_add & alu_zero | alu_slt;

assign alu2idu_addro = idu2alu_addr1 + idu2alu_addr2;

endmodule
